
/**
 * @Author: 陈瑞鹏
 * @Date:   2022-12-12 15:40:55
 * @Last Modified by:   陈瑞鹏
 * @Last Modified time: 2023-01-04 16:20:57
 */
import { GameSystem, GameSystemState } from "../../shared/game/GameSystem";
import { serviceProto, ServiceType } from "../../shared/protocols/serviceProto";
import { WsClient } from 'tsrpc-browser';
import { ClientInput, MsgClientInput } from "../../shared/protocols/client/MsgClientInput";
import { MsgFrame } from "../../shared/protocols/server/MsgFrame";
import gameCtr from "../gameCtr";
import BaseEle from "../../eles/baseEle";
import circleEle from "../../eles/circleEle";
import lineCom from "../../eles/lineCom";
import triangleCom from "../../eles/triangleCom";
import squareCom from "../../eles/squareCom";
import timeArrow from "../../eles/timeArrow";

export default class pvpCtr {

  client: WsClient<ServiceType>;
  gameSystem = new GameSystem();

  lastServerState: GameSystemState = this.gameSystem.state;
  lastRecvSetverStateTime = 0;
  selfPlayerId: number = -1;
  lastSn = 0;
  team: number = 2; //1右边 2 左边
  roomId: number; //房间号
  roomName: string; //房间名称

  public model: number = 1; //1 人人模式  2 人机模式
  /**
 * 元素 集合对象
 */
  baseEles: { [baseId: number]: BaseEle | undefined } = {};

  /**
   * 资源元素 集合对象
   */
  resBaseEle: { [baseId: number]: BaseEle | undefined } = {};

  cirCleEles: { [circleId: number]: circleEle | lineCom | triangleCom | squareCom } = {};

  arrowEles: { [arrowId: number]: timeArrow | undefined } = {};

  public isStarte: boolean = false; //是否开始

  public static ins: pvpCtr = null;

  constructor() {
    if (pvpCtr.ins == null) {
      pvpCtr.ins = this;
    }

    // console.log("constructor");
    let client = this.client = new WsClient(serviceProto, {
      server: `ws://47.97.98.15:3050`,
      // server: `ws://127.0.0.1:3050`,
      json: true
    })
    client.listenMsg('server/Frame', msg => { this._onServerSync(msg) });

    //模拟网络延迟 可通过 url 参数 lag=200 设置延迟
    let networkLag = parseInt(new URLSearchParams(location.search).get('lag') || '0') || 0;
    if (networkLag) {
      client.flows.preRecvDataFlow.push(async v => {
        await new Promise(rs => {
          setTimeout(rs, networkLag)
        });
        return v;
      })
      client.flows.preSendDataFlow.push(async v => {
        await new Promise(rs => { setTimeout(rs, networkLag) })
        return v;
      })
    }

    (window as any).gm = this;
  }

  get state() {
    return this.gameSystem.state;
  }

  /**
   * 获取当前玩家
   */
  get currentPlayerState() {
    return this.state.players.find(v => v.id == this.selfPlayerId);
  }

  /**加入房间 */
  async join(): Promise<void> {
    console.log("join", new Date().getTime());
    if (!this.client.isConnected) {
      let resConnect = await this.client.connect();
      if (!resConnect.isSucc) {
        await new Promise(rs => { setTimeout(rs, 2000) })
        return this.join();
      }
    }

    let ret = await this.client.callApi('Join', { playerId: gameCtr.ins.roleId, nickName: gameCtr.ins.nickName, avatar: gameCtr.ins.avatar, team: 1 });

    if (!ret.isSucc) {
      if (confirm(`加入房间失败\n${ret.err.message}\n是否重试?`)) {
        return this.join();
      } else {
        return;
      }
    }

    this.gameSystem.reset(ret.res.gameState);
    this.lastServerState = Object.merge(ret.res.gameState);
    this.lastRecvSetverStateTime = Date.now();
    this.selfPlayerId = ret.res.playerId;
    this.team = ret.res.team;
    this.roomId = Number(ret.res.roomId);
    this.roomName = "房间_" + this.roomId;

    gameCtr.ins.curScene.emit("joinSuc");
    console.log(this.selfPlayerId, "this.selfPlayerId");
  }



  /**退出 */
  async Leave(): Promise<void> {
    pvpCtr.ins = null;
    this.client.disconnect();
  }

  /**
   * 服务器帧
   * @param frame 
   */
  private _onServerSync(frame: MsgFrame) {
    //回滚至上一次的权威状态
    this.gameSystem.reset(this.lastServerState);

    //计算最新的权威状态
    for (let input of frame.inputs) {
      // if (input.type == "smallCircleMove") {
      //   console.log(input);
      // }
      this.gameSystem.applyInput(input);
    }

    this.lastServerState = Object.merge({}, this.gameSystem.state);
    this.lastRecvSetverStateTime = Date.now();

    //和解 = 权威状态 + 本地输入 （最新的本地预测状态）
    console.log(frame.lastSn, "frame.lastSn");
    let lastSn = frame.lastSn ?? -1;
    this.pendingInputMsgs.remove(v => v.sn <= lastSn);
    this.pendingInputMsgs.forEach(m => {
      m.inputs.forEach(v => {
        this.gameSystem.applyInput({
          ...v,
          playerId: this.selfPlayerId
        })
      })
    })
  }

  pendingInputMsgs: MsgClientInput[] = [];
  /**发送输入 */
  sendClientInput(input: ClientInput) {
    //已掉线或暂未加入，忽略本地输入
    if (!this.selfPlayerId || !this.client.isConnected) return;

    //构造消息
    let msg: MsgClientInput = {
      sn: ++this.lastSn,
      inputs: [input]
    }

    //向服务端发送输入
    this.pendingInputMsgs.push(msg);
    this.client.sendMsg('client/ClientInput', msg);

    //预测状态：本地立即应用输入
    this.gameSystem.applyInput({
      ...input,
      playerId: this.selfPlayerId
    })
  }

  //本地时间流逝（会被下一次服务器状态覆盖）
  localTimePast() {
    this.gameSystem.applyInput({
      type: "LocalTimePast",
      dt: Date.now() - this.lastRecvSetverStateTime
    })
    this.lastRecvSetverStateTime = Date.now();
  }
}